package com.techie.mdm.appbar.showcase.binding;

import org.springframework.binding.form.FormModel;
import org.springframework.richclient.form.binding.support.CustomBinding;
import org.springframework.rules.closure.Closure;
import jdesktop.appbar.Edge;
import jdesktop.appbar.Sides;

import javax.swing.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SidesCheckboxBinding extends CustomBinding
{
    private Sides sides;

    private JPanel  container;
    private Map<Edge, JCheckBox> checkboxes;

    private final UpdateBoxState updateState = new UpdateBoxState();

    public JCheckBox getCheckBoxForEdge(Edge edge)
    {
        return checkboxes.get(edge);
    }


    class UpdateBoxState implements Closure
    {
        private Sides   sides;

        public void update()
        {
            this.sides = getBoundValue();
            applyToCheckBoxes(this);
            this.sides = null;
        }


        public Object call(Object argument)
        {
            JCheckBox box = (JCheckBox) argument;
            Edge edge = getCheckBoxEdge(box);
            boolean state = sides.isSet(edge);
            box.setSelected(state);
            return argument;
        }
    }

    /**
     *
     * @param formModel
     * @param formPropertyPath
     */
    public SidesCheckboxBinding(FormModel formModel, String formPropertyPath)
    {
        super(formModel, formPropertyPath, Sides.class);
    }

    /**
     * Fetches the edge the checkbox is associated with.
     * @param box
     * @return
     */
    public Edge getCheckBoxEdge(JCheckBox box)
    {
        return (Edge) box.getClientProperty(Edge.class);
    }

    public void setCheckBoxEdge(JCheckBox box, Edge edge)
    {
        box.putClientProperty(Edge.class, edge);        
    }



    @Override
    /**
     * A new sides instance is bound.
     */
    protected void valueModelChanged(Object newValue)
    {
        sides = (Sides) newValue;
        updateState.update();
    }

    Sides getBoundValue()
    {
        return sides;
    }


    @Override
    protected JComponent doBindControl()
    {
        Map<Edge, JCheckBox> boxes = new HashMap<Edge, JCheckBox>();
        for (Edge edge : Edge.values())
        {
            JCheckBox box = getComponentFactory().createCheckBox("side."+edge.name());
            // todo - how to configure using standard form layout, e.g. icons, mnemonics etc..
            setCheckBoxEdge(box, edge);
            boxes.put(edge, box);
        }
        checkboxes = boxes;
        container = getComponentFactory().createPanel();
        return container;
    }

    public Map<Edge, JCheckBox> getCheckboxes()
    {
        Map<Edge,JCheckBox> map;
        if (checkboxes==null)
            map = Collections.emptyMap();
        else
            map = Collections.unmodifiableMap(checkboxes);
        return map;
    }

    /**
     * 
     */
    @Override
    protected void readOnlyChanged()
    {
        updateEnabledState();
    }

    /**
     * Sets the enabled properties of all the checkboxes.
     */
    @Override
    protected void enabledChanged()
    {
        updateEnabledState();
    }

    private void updateEnabledState()
    {
        final boolean enabled = isEnabled() && !isReadOnly();
        applyToCheckBoxes(new Closure()
        {
            public Object call(Object argument)
            {
                JComponent comp = (JComponent) argument;
                comp.setEnabled(enabled);
                return comp;
            }
        });
    }

    void applyToCheckBoxes(Closure c)
    {
        if (checkboxes!=null)
        for (JCheckBox box : checkboxes.values())
        {
            c.call(box);
        }
    }

}
